﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using Org.BouncyCastle.Asn1;

namespace TheClerk.Forms.Templates.raw.Envelope
{
    #region public AssertionType
    // Serialize() complete
    /// <summary>
    /// Identifies the OID or MIME-type of the Assertion
    /// </summary>
    /// <remarks>
    /// AssertionType ::= CHOICE {
    ///     oid OBJECT IDENTIFIER,
    ///     mimeType UTF8String }
    /// </remarks>
    public class AssertionType
    {
        private static readonly Regex OidRegex = new Regex(@"\A([0-2])(\.([0-9]+))+\z");

        internal int[] _oid;
        internal string mimeType;

        /// <summary>
        /// Type of data included in Assertion
        /// </summary>
        /// <param name="type">MIME-type or OID string</param>
        public AssertionType(string type = "application/octet-stream")
        {
            SetType(type);
        }

        /// <summary>
        /// What kind of data is in the attached Assertion?
        /// </summary>
        /// <param name="mytype">Either a MIME-type or OID string</param>
        /// <remarks>
        /// There's no functional difference between OIDs and MIME-types.
        /// The only reason it's defined this way is because of the ASN.1
        /// CHOICE semantic.
        /// </remarks>
        public void SetType(string mytype = "application/octet-string")
        {
            // MIME-type will be more common, so first
            if (mytype.IndexOf('/') > -1)
            {
                // This appears to be a MIME-type
                this.mimeType = mytype;
                this._oid = null;
                return;
            } // If it's not a MIME-type, it must be an OID
            else if (!OidRegex.IsMatch(mytype))
            {
                // not a MIME-type, not an OID
                throw new ArgumentException("Must be MIME-type or OID string");
            }
            var mc = mytype.Split(new char[] { '.' });
            _oid = new int[mc.Length];
            for (int i = 0; i < mc.Length; i++)
            {
                _oid[i] = Convert.ToInt32(mc[i]);
            }
        }

        /// <summary>
        /// Set this AssertionType to a particular OID
        /// </summary>
        /// <param name="mytype">The OID to specify</param>
        /// <exception cref="ArgumentNullException">mytype is null</exception>
        /// <exception cref="ArgumentException">mytype[0] is not 0, 1, or 2</exception>
        public void SetType(int[] mytype)
        {
            if (null == mytype)
                throw new ArgumentNullException("mytype");
            if (mytype[0] < 0 || mytype[0] > 2)
                throw new ArgumentException("First element of OID must be 0, 1, or 2");

            StringBuilder s = new StringBuilder();
            foreach (int i in mytype)
            {
                s.Append(i);
                s.Append(".");
            }
            // get rid of trailing "."
            s.Remove(s.Length - 1, 1);
            this.mimeType = s.ToString();
        }

        internal Stream Serialize()
        {
            Stream o = new MemoryStream();
            if (_oid != null)
            {
                var der = new DerObjectIdentifier(_oid);
                o.Write(der.GetDerEncoded(), 0, der.GetDerEncoded().Length);
            }
            else
            {
                var der = new DerUtf8String(this.mimeType);
                o.Write(der.GetDerEncoded(), 0, der.GetDerEncoded().Length);
            }
            o.Position = 0;
            return o;
        }

        internal Asn1Encodable ToAsn1Encodable()
        {
            return Asn1Object.FromStream(Serialize());
        }
    }
    #endregion
}
